#include "qt_ubutu_serial.h"

pthread_t tid;
//默认的温湿度阈值
int qt_hum = 70, qt_temp = 35;
/* 用于获取iic传递的温湿度值，flag:是否为自动模式
 * flag用于【使能】自动模式中低于阈值关闭报警 */
int ban_hum, ban_temp, flag = 0;
int h_g, h_s, t_g, t_s;
char msg[128] = "";
void *deal_cli_iic(void *arg);
void *deal_cli_spi(void *arg);
char buf[128] = {}; // 用于接收温湿度

char w_buf[] = {0xf, 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x0}; // 数码管位
char choose[] = {0x1, 0x2, 0x4, 0x8, 0x0};                                             // 数码管段

//延时函数
void delay_us(uint n)
{
    uint i, j;
    for (i = n; i > 0; i--)
        for (j = 114; j > 0; j--)
        {
        }
}

int main(int argc, const char *argv[])
{
    struct fd fdall;
    int state = 0;
    // 打开GPIO设备 LED1~3 风扇 蜂鸣器 震动马达
    fdall.fd1 = open("/dev/mycdev0", O_RDWR);
    if (fdall.fd1 < 0)
    {
        ERR_MSG("open");
        return 0;
    }
    // 打开i2c si7006设备
    fdall.fd2 = open("/dev/si7006", O_RDWR);
    if (fdall.fd2 < 0)
    {
        ERR_MSG("open");
        return 0;
    }
    //打开spi数码管设备
    fdall.fd3 = open("/dev/m74hc595", O_RDWR);
    {
        if (fdall.fd3 < 0)
        {
            ERR_MSG("open");
            return 0;
        }
    }
    // 创建流式套接字
    ubuntu_socket_create();
    // 由于自动获取,只需要定义几个结构体变量进行接收,需要时直接访问里面成员即可
    struct sockaddr_in cin;
    socklen_t addrlen = sizeof(cin);
    // 主线程负责处理QT客户端连接交互
START:
    /* 连接 QT客户端 将i2c驱动获取的全局温湿度传输给 QT客户端
       在QT界面显示 两位湿度值 + 两位温度值 */ 
    fdall.newfd = accept(sfd, (struct sockaddr *)&cin, &addrlen);
    if (fdall.newfd < 0)
    {
        ERR_MSG("accept");
        return -1;
    }
    // printf("[%s:%d] newfd = %d连接成功\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), fdall.newfd);
    // 创建线程与i2c驱动进行通信   获取温湿度 并保存到全局变量值
    if (pthread_create(&tid, NULL, deal_cli_iic, &fdall) != 0)
    {
        fprintf(stderr, "line:%d pthread_create error\n", __LINE__);
        return -1;
    }
    /* 创建线程与i2c驱动进行通信 将i2c驱动获取的全局温湿度解析
       在数码管上显示 两位湿度值 + 两位温度值 */  
    if (pthread_create(&tid, NULL, deal_cli_spi, &fdall) != 0)
    {
        fprintf(stderr, "line:%d pthread_create error\n", __LINE__);
        return -1;
    }

    while (1)
    {
        int ret = 0;
        state = !state;
        bzero(msg, sizeof(msg));
        // 循环接收qt手动控制的哪一个硬件的消息
        ret = recv(fdall.newfd, msg, sizeof(msg), 0);
        if (ret < 0)
        {
            ERR_MSG("recv");
            return 0;
        }
        if (ret == 0)
        {
            fprintf(stderr, "[%s:%d]newfd = %d 客户端下线\n",
                    inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), fdall.newfd);
            goto START;
        }
        char T = *msg;
        // 存放温湿度
        char substr[4] = {};
        strncpy(substr, (msg + 1), 4);
        // printf("T-'0' = %d\n", T - '0');
        switch (T - '0')
        {
        case LED1: // 接收判断为1,为LED1
            ioctl(fdall.fd1, LED1_L, &state);
            break;
        case LED2: // 接收判断为2,为LED2
            ioctl(fdall.fd1, LED2_L, &state);
            break;
        case LED3: // 接收判断为3,为LED3
            ioctl(fdall.fd1, LED3_L, &state);
            break;
        case FAN: // 接收判断为4,为风扇
            ioctl(fdall.fd1, FAN_L, &state);
            break;
        case BUZ: // 接收判断为5,为蜂鸣器
            ioctl(fdall.fd1, BUZ_L, &state);
            break;
        case MOT: // 接收判断为6,为马达
            ioctl(fdall.fd1, MOT_L, &state);
            break;
        case TEMP: // 接收判断为7,为温湿度
            /* 协议为 [cmd][两位温度][两位湿度]
             * 因此从下标[1]截取温度字符串并转换为整形
             *     从下标[4]截取湿度字符串并转换为整形 */
            strncpy(substr, (msg + 1), 2);
            qt_temp = atoi(substr);
            qt_hum = atoi(msg + 4);
            break;
        case AUTO: // 接收判断为8,切换到自动模式
            flag = 1;
            break;
        case HAND: // 接收判断为9,切换到手动模式
            flag = 0;
            break;
        default:
            break;
        }
    }
    // 关闭文件描述符
    close(fdall.newfd);
    return 0;
}
// 子线程进行与开发板交互，收消息发送给QT、判断阈值、查过阈值发送给开发板、
void *deal_cli_spi(void *arg)
{
    int hum1, tem1;
    // 分离线程，自动回收线程资源
    pthread_detach(pthread_self());
    int fd1 = ((struct fd *)arg)->fd1;
    int fd2 = ((struct fd *)arg)->fd2;
    int fd3 = ((struct fd *)arg)->fd3;
    int newfd = ((struct fd *)arg)->newfd;
    while (1)
    {
        //发送断码与位码显示正确值
        ioctl(fd3, SPI_L, &choose[0]);
        //将个位或者十位对应到断码数组中
        ioctl(fd3, SPI_L, &w_buf[h_s + 1]);
        //延时 消除 【鬼影】 消影的另外两种办法:http://t.csdn.cn/DWbsi
        delay_us(2500);

        ioctl(fd3, SPI_L, &choose[1]);
        ioctl(fd3, SPI_L, &w_buf[h_g + 1]);

        delay_us(2500);

        ioctl(fd3, SPI_L, &choose[2]);
        ioctl(fd3, SPI_L, &w_buf[t_s + 1]);
        delay_us(2500);

        ioctl(fd3, SPI_L, &choose[3]);
        ioctl(fd3, SPI_L, &w_buf[t_g + 1]);
        delay_us(2500);
    }
}

// 获取温湿度，并存贮他们的个位与十位
void *deal_cli_iic(void *arg)
{
    int hum1, tem1;
    // 分离线程，自动回收线程资源
    pthread_detach(pthread_self());
    int fd1 = ((struct fd *)arg)->fd1;
    int fd2 = ((struct fd *)arg)->fd2;
    int fd3 = ((struct fd *)arg)->fd3;
    int newfd = ((struct fd *)arg)->newfd;
    while (1)
    {
        // 接收开发板的温湿度数据
        ioctl(fd2, TEMP_L, &ban_hum);
        ioctl(fd2, HUM_L, &ban_temp);
        // 大小端转换
        ban_hum = ntohs(ban_hum);
        ban_temp = ntohs(ban_temp);
        // 计算数据
        hum1 = 125.0 * ban_hum / 65536 - 6;
        tem1 = 175.72 * ban_temp / 65536 - 46.85;

        // 存储湿度的个位十位
        h_g = hum1 % 10;
        h_s = hum1 / 10 % 10;
        // 存储温度的个位十位
        t_g = tem1 % 10;
        t_s = tem1 / 10 % 10;
        // 拼接温度湿度为两个字符串，发送给QT
        char te[2], hu[2];
        te[0] = t_s + '0';
        te[1] = t_g + '0';
        hu[0] = h_s + '0';
        hu[1] = h_g + '0';
        // 将转换后的数据发送给QT进行界面显示
        if (send(newfd, te, sizeof(te), 0) < 0)
        {
            ERR_MSG("send");
            return 0;
        }
        if (send(newfd, hu, sizeof(hu), 0) < 0)
        {
            ERR_MSG("send");
            return 0;
        }
        // 比较温湿度与阈值，如果大于则发送标志给到开发板，进行报警设置
        if (hum1 >= qt_hum || tem1 >= qt_temp)
        {
            int state;
            state = LED1;
            ioctl(fd1, GPIO_ON, &state);
            state = LED2;
            ioctl(fd1, GPIO_ON, &state);
            state = LED3;
            ioctl(fd1, GPIO_ON, &state);
            state = FAN;
            ioctl(fd1, GPIO_ON, &state);
            state = BUZ;
            ioctl(fd1, GPIO_ON, &state);
            state = MOT;
            ioctl(fd1, GPIO_ON, &state);
        }
        //通过qt发送的AUTO指令置位flag 以 使能 【低于阈值停止报警功能】
        if (flag == 1)
        {
            if (hum1 <= qt_hum && tem1 <= qt_temp)
            {
                int state;
                state = LED1;
                ioctl(fd1, GPIO_OFF, &state);
                state = LED2;
                ioctl(fd1, GPIO_OFF, &state);
                state = LED3;
                ioctl(fd1, GPIO_OFF, &state);
                state = FAN;
                ioctl(fd1, GPIO_OFF, &state);
                state = BUZ;
                ioctl(fd1, GPIO_OFF, &state);
                state = MOT;
                ioctl(fd1, GPIO_OFF, &state);
            }
        }
            sleep(1);
    }
}
